using System;
using System.Collections.Generic;
using XenoGears.Collections.Dictionaries;
using XenoGears.Functional;
using XenoGears.Collections.Observable;
using XenoGears.Assertions;

namespace Jink.Core.Converters.Context
{
    public class ConversionContext : IDisposable
    {
        public bool IsRoot { get { return Parent != null; } }
        public ConversionContext Parent { get; private set; }
        public IEnumerable<ConversionContext> Parents { get { return Parent.Unfold(j => j.Parent, j => j != null); } }

        private readonly Dictionary<String, Object> _cookies;
        public Dictionary<String, Object> Cookies { get { return _cookies; } }

        private readonly ReadOnlyDictionary<String, Type> _goal;
        public ReadOnlyDictionary<String, Type> Goal { get { return _goal; } }

        private ConversionContext(ConversionContext parent, IEnumerable<ConversionContextEntry> input, IDictionary<String, Type> goal, IDictionary<String, Object> cookies)
        {
            Parent = parent;
            _entries = input.Observe();
            _goal = Goal;
            _cookies = cookies == null ? new Dictionary<String, Object>() : cookies.ToDictionary();

            EventsRegistry.OnInit.ForEach(h => h(this));
            EventsRegistry.OnChanged.ForEach(h => { _entries.ListChanged += (o, e) => h(this, e); });
        }

        private static Object _currentLock = new Object();
        public static ConversionContext Current { get; private set; }

        internal static ConversionContext New(IEnumerable<ConversionContextEntry> input, IDictionary<String, Type> goal)
        {
            return New(input, goal, null);
        }

        internal static ConversionContext New(IEnumerable<ConversionContextEntry> input, IDictionary<String, Type> goal, IDictionary<String, Object> cookies)
        {
            lock (_currentLock)
            {
                _currentLock = new ConversionContext(Current, input, goal, cookies);
            }

            return Current;
        }

        public void Dispose()
        {
            lock (_currentLock)
            {
                (Current == this).AssertTrue();
                Current = Current.Parent;
            }
        }

        private readonly IObservableList<ConversionContextEntry> _entries;
        public IObservableList<ConversionContextEntry> Entries { get { return _entries; } }
    }
}